// SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
// SPDX-License-Identifier: GPL-3.0-only

package trace_dao

import (
	"strings"
	"time"

	"gitcode.com/opendragonfly/df_proto_gen_go.git/trace_api"
	"github.com/dgraph-io/badger/v4"
)

const (
	_SERVICE_NAME_SPACE = "service"
)

type _ServiceTable struct{}

func (table *_ServiceTable) genKey(serviceInfo *trace_api.ServiceInfo, timeStamp int64) []byte {
	dayStr := time.UnixMilli(timeStamp).Format(time.DateOnly)
	key := strings.Join([]string{_SERVICE_NAME_SPACE, dayStr, serviceInfo.ServiceName, serviceInfo.ServiceVersion}, "\t")
	return []byte(key)
}

func (table *_ServiceTable) Insert(txn *badger.Txn, serviceInfo *trace_api.ServiceInfo, rootSpanName string, timeStamp int64, keepTraceDay uint) error {
	key := table.genKey(serviceInfo, timeStamp)
	item, err := txn.Get(key)
	if err != nil {
		if err == badger.ErrKeyNotFound {
			entry := badger.NewEntry(key, []byte(rootSpanName)).WithTTL(time.Duration(keepTraceDay) * time.Hour * 24)
			return txn.SetEntry(entry)
		}
		return err
	}
	value, err := item.ValueCopy(nil)
	if err != nil {
		return err
	}
	nameList := strings.Split(string(value), "\t")
	for _, name := range nameList {
		if name == rootSpanName {
			return nil
		}
	}
	nameList = append(nameList, rootSpanName)
	newValue := strings.Join(nameList, "\t")
	entry := badger.NewEntry(key, []byte(newValue)).WithTTL(time.Duration(keepTraceDay) * time.Hour * 24)
	return txn.SetEntry(entry)
}

func (table *_ServiceTable) ListService(txn *badger.Txn, fromTime, toTime int64) ([]*trace_api.ServiceInfo, error) {
	dayStrMap := map[string]bool{}
	for curTime := fromTime; curTime <= toTime; curTime += 24 * 3600 * 1000 {
		dayStr := time.UnixMilli(curTime).Format(time.DateOnly)
		dayStrMap[dayStr] = true
	}
	dayStr := time.UnixMilli(toTime).Format(time.DateOnly)
	dayStrMap[dayStr] = true

	tmpMap := map[string]*trace_api.ServiceInfo{}
	for dayStr := range dayStrMap {
		keyPrefix := strings.Join([]string{_SERVICE_NAME_SPACE, dayStr}, "\t")
		options := badger.IteratorOptions{
			PrefetchValues: false,
			PrefetchSize:   100,
			Reverse:        false,
			AllVersions:    false,
			Prefix:         []byte(keyPrefix),
		}
		iter := txn.NewIterator(options)
		defer iter.Close()

		for iter.Rewind(); iter.Valid(); iter.Next() {
			item := iter.Item()
			if item.IsDeletedOrExpired() {
				continue
			}
			keyData := item.KeyCopy(nil)
			parts := strings.Split(string(keyData), "\t")
			if len(parts) == 4 {
				tmpMap[parts[2]+" "+parts[3]] = &trace_api.ServiceInfo{
					ServiceName:    parts[2],
					ServiceVersion: parts[3],
				}
			}
		}
	}

	retList := []*trace_api.ServiceInfo{}
	for _, info := range tmpMap {
		retList = append(retList, info)
	}
	return retList, nil
}

func (table *_ServiceTable) ListRootName(txn *badger.Txn, serviceInfo *trace_api.ServiceInfo, fromTime, toTime int64) ([]string, error) {
	dayStrMap := map[string]bool{}
	for curTime := fromTime; curTime <= toTime; curTime += 24 * 3600 * 1000 {
		dayStr := time.UnixMilli(curTime).Format(time.DateOnly)
		dayStrMap[dayStr] = true
	}
	dayStr := time.UnixMilli(toTime).Format(time.DateOnly)
	dayStrMap[dayStr] = true

	tmpMap := map[string]bool{}

	for dayStr, _ := range dayStrMap {
		key := strings.Join([]string{_SERVICE_NAME_SPACE, dayStr, serviceInfo.ServiceName, serviceInfo.ServiceVersion}, "\t")
		item, err := txn.Get([]byte(key))
		if err != nil {
			if err == badger.ErrKeyNotFound {
				continue
			}
			return nil, err
		}
		if item.IsDeletedOrExpired() {
			continue
		}
		value, err := item.ValueCopy(nil)
		if err != nil {
			return nil, err
		}
		nameList := strings.Split(string(value), "\t")
		for _, name := range nameList {
			tmpMap[name] = true
		}
	}
	retList := []string{}
	for name := range tmpMap {
		retList = append(retList, name)
	}
	return retList, nil
}
